package com.zh.freechat.config.filter;

import com.zh.freechat.domain.auth.LoginUser;
import com.zh.freechat.infrastructure.LoginAuthenticationApi;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static com.zh.webcommon.HeaderConst.HEADER_AUTH;
import static com.zh.webcommon.HeaderConst.HEADER_AUTH_PREFIX;

/**
 * 访问接口时，验证token是否有效
 *
 * @author ZH
 * @date 16:40 2021/8/1
 */
@AllArgsConstructor
@Slf4j
public class AuthTokenFilter extends OncePerRequestFilter implements Ordered {
    private final String[] whitelist;

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String uri = request.getRequestURI();
        // 通配符路径匹配 ("/static/**", "*.html", "*.js", "/actuator/**")
        if (uri.startsWith("/static/") || uri.startsWith("/actuator/") || uri.endsWith(".html") || uri.endsWith(".js")) {
            return true;
        }
        return ArrayUtils.contains(whitelist, uri);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String uri = request.getRequestURI();
        log.debug("into jwttoken verification filter ... uri = {}", uri);

        String authValue = request.getHeader(HEADER_AUTH);

        if (StringUtils.hasText(authValue)) {
            log.trace("Header Authorization 有值");
            LoginUser loginUser = null;
            //如果token无效或过期，则返回前端401
            try {
                loginUser = verifyTokenGetUserPrincipal(authValue);
            } catch (BadCredentialsException ex) {
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.getWriter().write(ex.getMessage());
                return;
            }

            if (loginUser != null) {
                List<GrantedAuthority> authorities = loginUser.getRoles() == null ? Collections.emptyList() : loginUser.getRoles().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());

                //新生成的authenticationToken已经是isAuthenticated的了
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, authorities);
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }

        filterChain.doFilter(request, response);
        log.debug("out of filter ...");
    }

    private LoginUser verifyTokenGetUserPrincipal(@NotNull String authValue) throws BadCredentialsException {
        if (!authValue.startsWith(HEADER_AUTH_PREFIX)) {
            log.warn("Authorization header值格式错误");
            return null;
        }
        var token = authValue.substring(HEADER_AUTH_PREFIX.length()).trim();
        return LoginAuthenticationApi.verifyToken(token);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
